hyper-tungstenite
This crate allows hyper
servers to accept websocket connections, backed by tungstenite
.
The upgrade
function allows you to upgrade a HTTP connection to a websocket connection.
It returns a HTTP response to send to the client, and a future that resolves to a WebSocketStream
.
The response must be sent to the client for the future to be resolved.
In practise this means that you must spawn the future in a different task.
Note that the upgrade
function itself does not check if the request is actually an upgrade request.
For simple cases, you can check this using the is_upgrade_request
function before calling upgrade
.
For more complicated cases where the server should support multiple upgrade protocols,
you can manually inspect the Connection
and Upgrade
headers.
Example
use futures::{sink::SinkExt, stream::StreamExt};
use hyper::{Body, Request, Response};
use hyper_tungstenite::{tungstenite, HyperWebsocket};
use std::convert::Infallible;
use tungstenite::Message;
type Error = Box<dyn std::error::Error + Send + Sync + 'static>;
async fn handle_request(request: Request<Body>) -> Result<Response<Body>, Error> {
if hyper_tungstenite::is_upgrade_request(&request) {
let (response, websocket) = hyper_tungstenite::upgrade(request, None)?;
tokio::spawn(async move {
if let Err(e) = serve_websocket(websocket).await {
eprintln!("Error in websocket connection: {}", e);
}
});
Ok(response)
} else {
Ok(Response::new(Body::from("Hello HTTP!")))
}
}
async fn serve_websocket(websocket: HyperWebsocket) -> Result<(), Error> {
let mut websocket = websocket.await?;
while let Some(message) = websocket.next().await {
match message? {
Message::Text(msg) => {
println!("Received text message: {}", msg);
websocket.send(Message::text("Thank you, come again.")).await?;
},
Message::Binary(msg) => {
println!("Received binary message: {:02X?}", msg);
websocket.send(Message::binary(b"Thank you, come again.".to_vec())).await?;
},
Message::Ping(msg) => {
println!("Received ping message: {:02X?}", msg);
},
Message::Pong(msg) => {
println!("Received pong message: {:02X?}", msg);
}
Message::Close(msg) => {
if let Some(msg) = &msg {
println!("Received close message with code {} and message: {}", msg.code, msg.reason);
} else {
println!("Received close message");
}
},
}
}
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), Error> {
let addr: std::net::SocketAddr = "[::1]:3000".parse()?;
println!("Listening on http://{}", addr);
hyper::Server::bind(&addr).serve(hyper::service::make_service_fn(|_connection| async {
Ok::<_, Infallible>(hyper::service::service_fn(handle_request))
})).await?;
Ok(())
}